#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <base64.h>
#include <quotedprintable.h>

// =?gb2312?B?tPC4tDogUmU6IM22td3W0NDE1ti148/uxL+8xruuvfi2yLHto6g01MK33aOp?=

#define ADVANCE_CHAR()                          \
    do {                                        \
        ++i;                                    \
        if (i >= inlen)                         \
            goto raw_subject;                   \
    } while (0)

int
subject_parse(const char *in,
              int         inlen,
              char       *out,
              char       *coding)
{
    int     i, j;
    char   *p;
    bool    base64_p;
    size_t  outlen;

    if (inlen < 0)
        inlen = strlen(in);

    *out    = 0;
    *coding = 0;

    if (inlen == 0)
        return 0;

    if (in[0] == '=' || in[inlen - 1] == '=') {
        // fetching the coding
        if (in[1] == '?') {
            i = 2;
            p = coding;
            while (i < inlen && in[i] != '?')
                *p++ = in[i++];
            if (i == inlen)
                goto raw_subject;

            *p = 0;

            ADVANCE_CHAR();

            // base64 or quoted-printable
            if (in[i] == 'B')
                base64_p = true;
            else if (in[i] == 'Q')
                base64_p = false;
            else
                goto raw_subject;

            ADVANCE_CHAR();

            if (in[i] != '?')
                goto raw_subject;

            ADVANCE_CHAR();

            j = i;
            while (j < inlen && in[j] != '?')
                ++j;

            if (j >= inlen)
                goto raw_subject;

            if (i < j) {
                if (base64_p) {
                    outlen = BASE64_LENGTH(j - i) + 1;
                    if (base64_decode(in + i, j - i, out, &outlen)) {
                        out[outlen] = 0;
                        return outlen;
                    }
                    else
                        return -1;
                }
                else {
                    return quoted_printable_decode(in + i, j - i, out, NULL);
                }
            }
        }
    }

raw_subject:
    memcpy(out, in, inlen);
    out[inlen] = 0;
    return inlen;
}
